package m.html.list;

import com.alibaba.fastjson.JSONArray;
import m.common.model.util.QueryCondition;
import m.html.element.*;
import m.html.element.attr.InputType;
import m.html.element.other.DisplayItem;
import m.html.element.other.IElement;
import m.html.element.other.InputElement;
import m.system.util.DateUtil;
import m.system.util.ObjectUtil;
import m.system.util.StringUtil;

import java.util.*;

public class ListSearch {

	private final boolean isField;
	private boolean notQuery;
	private final String title;
	private String titleDescription;
	private String inputDescription;
	private final String field;
	private final SearchType type;
	private InputElement element;
	private List<DisplayItem> disItems;
	private String dictType;
	private boolean dictTree=false;
	private int span;
	
	private ListSearch(boolean isField,String title,String field,SearchType type,List<DisplayItem> disItems) {
		this.isField=isField;
		this.title=title;
		this.field=field;
		this.type=type;
		this.disItems=disItems;
		this.span=1;
		this.notQuery=false;
	}
	/**
	 * 自定义查询条件, 不会根据field去过滤列表, 需要在构建result时自行添加查询条件
	 */
	public static ListSearch custom(String title,String field) {
		return custom(title,field,SearchType.STR_EQ);
	}
	/**
	 * 自定义查询条件, 不会根据field去过滤列表, 需要在构建result时自行添加查询条件
	 */
	public static ListSearch custom(String title,String field,SearchType type) {
		return custom(title,field,type, new ArrayList<>());
	}
	/**
	 * 自定义查询条件, 不会根据field去过滤列表, 需要在构建result时自行添加查询条件
	 */
	public static ListSearch custom(String title,String field,List<DisplayItem> disItems) {
		return custom(title,field,SearchType.STR_EQ,disItems);
	}
	/**
	 * 自定义查询条件, 不会根据field去过滤列表, 需要在构建result时自行添加查询条件
	 */
	public static ListSearch custom(String title,String field,SearchType type,List<DisplayItem> disItems) {
		return new ListSearch(false,title,field,type,disItems);
	}
	/**
	 * field查询条件, 会根据field去过滤列表
	 */
	public static ListSearch field(String title,String field) {
		return field(title,field,SearchType.STR_EQ);
	}
	/**
	 * field查询条件, 会根据field去过滤列表
	 */
	public static ListSearch field(String title,String field,SearchType type) {
		return field(title,field,type, new ArrayList<>());
	}
	/**
	 * field查询条件, 会根据field去过滤列表
	 */
	public static ListSearch field(String title,String field,List<DisplayItem> disItems) {
		return field(title,field,SearchType.STR_EQ,disItems);
	}
	/**
	 * field查询条件, 会根据field去过滤列表
	 */
	public static ListSearch field(String title,String field,SearchType type,List<DisplayItem> disItems) {
		return new ListSearch(true,title,field,type,disItems);
	}
	public ListSearch setElement(InputElement element) {
		this.element=element;
		return this;
	}
	public ListSearch setSpan(int span) {
		this.span = span;
		return this;
	}

	public boolean isNotQuery() {
		return notQuery;
	}

	/**
	 * 设置是否支持反向查询
	 */
	public ListSearch setNotQuery(boolean notQuery) {
		this.notQuery = notQuery;
		return this;
	}

	public String getTitleDescription() {
		return titleDescription;
	}
	/**
	 * 设置标题描述文字
	 */
	public ListSearch setTitleDescription(String titleDescription) {
		this.titleDescription = titleDescription;
		return this;
	}

	public String getInputDescription() {
		return inputDescription;
	}
	/**
	 * 设置输入描述文字
	 */
	public ListSearch setInputDescription(String inputDescription) {
		this.inputDescription = inputDescription;
		return this;
	}

	public int getSpan() {
		return span;
	}
	public boolean isField() {
		return isField;
	}
	public String getTitle() {
		return title;
	}
	public String getField() {
		return field;
	}
	public SearchType getType() {
		return type;
	}
	public ListSearch addDisItem(DisplayItem item) {
		this.disItems.add(item);
		return this;
	}
	public ListSearch addDisItem(String value,String title) {
		this.disItems.add(DisplayItem.value(value, title));
		return this;
	}
	public ListSearch setDisItems(List<DisplayItem> list) {
		this.disItems=list;
		return this;
	}
	public ListSearch setDictType(String dictType) {
		this.dictType = dictType;
		return this;
	}
	public ListSearch setDictTree(boolean dictTree){
		this.dictTree=dictTree;
		return this;
	}
	public String getDictType() {
		return dictType;
	}
	public List<DisplayItem> getDisItems(){
		return this.disItems;
	}
	public Object[] toCondition(String realField,Map<String,String> params) {
		StringBuilder cond=new StringBuilder();
		List<Object> ps= new ArrayList<>();
		if(this.type.toString().contains("RANGE")) {
			if(null!=params.get(this.field+"_down")) {
				String valueDown=String.valueOf(params.get(this.field+"_down"));
				if(!StringUtil.isSpace(valueDown)) {
					cond.append(realField).append(">=?");
					if(this.type==SearchType.NUMBER_RANGE||this.type==SearchType.HIDDEN_NUMBER_RANGE) 
						ps.add(Double.parseDouble(valueDown));
					else if(this.type==SearchType.DATE_RANGE||this.type==SearchType.HIDDEN_DATE_RANGE) 
						ps.add(DateUtil.getStartDay((Date) ObjectUtil.convert(Date.class, valueDown)));
					else if(this.type==SearchType.DATETIME_RANGE||this.type==SearchType.HIDDEN_DATETIME_RANGE)
						ps.add(ObjectUtil.convert(Date.class, valueDown));
					else if(this.type==SearchType.YEAR_RANGE||this.type==SearchType.HIDDEN_YEAR_RANGE)
						ps.add(valueDown);
				}
			}
			if(null!=params.get(this.field+"_up")) {
				String valueUp=String.valueOf(params.get(this.field+"_up"));
				if(!StringUtil.isSpace(valueUp)) {
					if(cond.length()>0) cond.append(" and ");
					cond.append(realField).append("<=?");
					if(this.type==SearchType.NUMBER_RANGE||this.type==SearchType.HIDDEN_NUMBER_RANGE) 
						ps.add(Double.parseDouble(valueUp));
					else if(this.type==SearchType.DATE_RANGE||this.type==SearchType.HIDDEN_DATE_RANGE) 
						ps.add(DateUtil.getEndDay((Date) ObjectUtil.convert(Date.class, valueUp)));
					else if(this.type==SearchType.DATETIME_RANGE||this.type==SearchType.HIDDEN_DATETIME_RANGE) 
						ps.add(ObjectUtil.convert(Date.class, valueUp));
					else if(this.type==SearchType.YEAR_RANGE||this.type==SearchType.HIDDEN_YEAR_RANGE)
						ps.add(valueUp);
				}
			}
		}else {
			if(null!=params.get(this.field)) {
				String value=String.valueOf(params.get(this.field));
				if(!StringUtil.isSpace(value)) {
					if(value.matches("^\\[.*\\]$")) {
						JSONArray arr=JSONArray.parseArray(value);
						if(!arr.isEmpty()) {
							cond.append("(1=2 ");
							for (Object o : arr) {
								cond.append(" or ").append(realField).append("=?");
								ps.add(o);
							}
							cond.append(")");
						}
					}else if(this.type==SearchType.STR_MANY_LIKE||this.type==SearchType.HIDDEN_MANY_LIKE) {
						String[] arr=value.split("\\s+");
						cond.append("(1=2");
						for (String s : arr) {
							if(StringUtil.isSpace(s)) continue;
							String[] ss=s.split("\\++");
							if(ss.length>0&&StringUtil.notSpace(ss[0]))
								cond.append(" or 1=1");
							for(String v : ss) {
								if(StringUtil.isSpace(v)) continue;
								cond.append(" and instr(").append(realField).append(",?)>0");
								ps.add(v);
							}
						}
						cond.append(")");
					}else if(this.type==SearchType.STR_LIKE||this.type==SearchType.HIDDEN_LIKE) {
						cond.append("instr(").append(realField).append(",?)>0");
						ps.add(value);
					}else {
						cond.append(realField).append("=?");
						ps.add(value);
					}
				}
			}
		}
		String not=StringUtil.noSpace(params.get(this.field+"_not"));
		return new Object[] {"true".equals(not)?("not "+cond):cond.toString(),ps};
	}
	public List<QueryCondition> toCondition(Map<String,String> params){
		List<QueryCondition> cons= new ArrayList<>();
		if(this.type.toString().contains("RANGE")) {
			if(null!=params.get(this.field+"_down")) {
				String valueDown=String.valueOf(params.get(this.field+"_down")).trim();
				if(!StringUtil.isSpace(valueDown)) {
					if(this.type==SearchType.NUMBER_RANGE||this.type==SearchType.HIDDEN_NUMBER_RANGE) 
						cons.add(QueryCondition.ge(this.field, Double.parseDouble(valueDown)));
					else if(this.type==SearchType.DATE_RANGE||this.type==SearchType.HIDDEN_DATE_RANGE) 
						cons.add(QueryCondition.ge(this.field, DateUtil.getStartDay((Date) ObjectUtil.convert(Date.class, valueDown))));
					else if(this.type==SearchType.DATETIME_RANGE||this.type==SearchType.HIDDEN_DATETIME_RANGE) 
						cons.add(QueryCondition.ge(this.field, ObjectUtil.convert(Date.class, valueDown)));
					else if(this.type==SearchType.YEAR_RANGE||this.type==SearchType.HIDDEN_YEAR_RANGE)
						cons.add(QueryCondition.ge(this.field, valueDown));
				}
			}
			if(null!=params.get(this.field+"_up")) {
				String valueUp=String.valueOf(params.get(this.field+"_up")).trim();
				if(!StringUtil.isSpace(valueUp)) {
					if(this.type==SearchType.NUMBER_RANGE||this.type==SearchType.HIDDEN_NUMBER_RANGE) 
						cons.add(QueryCondition.le(this.field, Double.parseDouble(valueUp)));
					else if(this.type==SearchType.DATE_RANGE||this.type==SearchType.HIDDEN_DATE_RANGE) 
						cons.add(QueryCondition.le(this.field, DateUtil.getEndDay((Date) ObjectUtil.convert(Date.class, valueUp))));
					else if(this.type==SearchType.DATETIME_RANGE||this.type==SearchType.HIDDEN_DATETIME_RANGE)
						cons.add(QueryCondition.le(this.field, ObjectUtil.convert(Date.class, valueUp)));
					else if(this.type==SearchType.YEAR_RANGE||this.type==SearchType.HIDDEN_YEAR_RANGE)
						cons.add(QueryCondition.le(this.field, valueUp));
				}
			}
		}else {
			if(null!=params.get(this.field)) {
				String value=String.valueOf(params.get(this.field)).trim();
				if(!StringUtil.isSpace(value)) {
					if(value.matches("^\\[.*\\]$")){
						JSONArray arr=JSONArray.parseArray(value);
						if(!arr.isEmpty()) {
							QueryCondition[] qc=new QueryCondition[arr.size()];
							for(int i=0;i<qc.length;i++) {
								qc[i]=QueryCondition.eq(this.field, arr.get(i));
							}
							cons.add(QueryCondition.or(qc));
						}
					}else if(this.type==SearchType.STR_MANY_LIKE||this.type==SearchType.HIDDEN_MANY_LIKE) {
						String[] arr=value.split("\\s+");
						QueryCondition[] qc=new QueryCondition[arr.length];
						for(int i=0;i<arr.length;i++) {
							if(StringUtil.isSpace(arr[i])) continue;
							String[] ss=arr[i].split("\\++");
							QueryCondition[] qs=new QueryCondition[ss.length];
							for(int n=0;n<ss.length;n++) {
								if(StringUtil.isSpace(ss[n])) continue;
								qs[n] = QueryCondition.like(this.field, ss[n]);
							}
							qc[i] = QueryCondition.and(qs);
						}
						cons.add(QueryCondition.or(qc));
					}else if(this.type==SearchType.STR_LIKE||this.type==SearchType.HIDDEN_LIKE) {
						cons.add(QueryCondition.like(this.field, value));
					}else {
						cons.add(QueryCondition.eq(this.field, value));
					}
				}
			}
		}
		String not=StringUtil.noSpace(params.get(this.field+"_not"));
		if("true".equals(not)){
			return Collections.singletonList(QueryCondition.not(QueryCondition.and(cons.toArray(new QueryCondition[0]))));
		}else {
			return cons;
		}
	}
	public Element toElement() {//输出组件
		Element ele=null;
		if(this.type.toString().indexOf("HIDDEN")==0) {
			return null;
		}else if(null!=this.element) {
			this.element.setField("inputParams['"+this.field+"']");
			ele=this.element;
		}else if(!StringUtil.isSpace(this.dictType)) {
			if(this.dictTree){
				ele=Element.tag("a-tree-select").A("v-model:value","inputParams['"+this.field+"']")
						.A(":tree-data","dictTreeOptions('"+this.dictType+"')").A("multiple").A("placeholder","请选择... 支持多选")
						.A(":filterTreeNode","treeSelectFilter").A("style","width:100%;").A(":showArrow","true")
						.A("max-tag-count","responsive");

			}else{
				ele=Element.tag("a-select").A("v-model:value","inputParams['"+this.field+"']")
						.A(":options","dictOptions('"+this.dictType+"')").A("mode","multiple").A("placeholder","请选择... 支持多选")
						.A(":filter-option","selectFilter").A("style","width:100%;").A(":showArrow","true")
						.A("max-tag-count","responsive");
			}
		}else if(!this.disItems.isEmpty()) {
			List<IElement> rs= new ArrayList<>();
			for(DisplayItem di : this.disItems) {
				rs.add(
					Element.tag("a-select-option").A("key",di.getValue()).A("value",di.getValue()).E(new Text(di.getTitle()))
				);
			}
			ele=Element.tag("a-select").A("v-model:value","inputParams['"+this.field+"']").A("mode","multiple").A("placeholder","请选择... 支持多选")
				.A(":filter-option","selectFilter").A("style","width:100%;").A(":showArrow","true")
				.A("max-tag-count","responsive")
				.E(rs);
		}else if(this.type==SearchType.STR_EQ||this.type==SearchType.STR_LIKE){//字符串类型
			ele=new Input("inputParams['"+this.field+"']");
		}else if(this.type==SearchType.STR_MANY_LIKE){//字符串类型
			ele=new Input("inputParams['"+this.field+"']", InputType.TEXTAREA);
		}else if(this.type==SearchType.NUMBER_RANGE) {//数字类型
			ele=Element.tag("a-space-compact").A("block")
				.E(new InputNumber("inputParams['"+this.field+"_down']").setPlaceholder("下限").setExtend("style=\"width:100%\""))
				.E(Element.tag("a-input").A("placeholder","~").A("disabled").A("class","input-split"))
				.E(new InputNumber("inputParams['"+this.field+"_up']").setPlaceholder("上限").setExtend("style=\"width:100%\""));
		}else if(this.type==SearchType.DATE_RANGE||this.type==SearchType.DATETIME_RANGE) {
			boolean flag=this.type==SearchType.DATETIME_RANGE;
			ele=Element.tag("a-space-compact").A("block")
				.E(new DatePicker("inputParams['"+this.field+"_down']",flag).setPlaceholder("下限").setExtend("style=\"width:100%\""))
				.E(Element.tag("a-input").A("placeholder","~").A("disabled").A("class","input-split"))
				.E(new DatePicker("inputParams['"+this.field+"_up']",flag).setPlaceholder("上限").setExtend("style=\"width:100%\""));
		}else if(this.type==SearchType.YEAR_RANGE){//年选择
			ele=Element.tag("a-space-compact").A("block")
				.E(new YearPicker("inputParams['"+this.field+"_down']").setPlaceholder("下限").setExtend("style=\"width:100%\""))
				.E(Element.tag("a-input").A("placeholder","~").A("disabled").A("class","input-split"))
				.E(new YearPicker("inputParams['"+this.field+"_up']").setPlaceholder("上限").setExtend("style=\"width:100%\""));
		}
		if(null!=ele&&StringUtil.notSpace(inputDescription))
			return Element.tag("a-tooltip")
				.E(Element.tag("template").A("#title").E(inputDescription))
				.E(ele);
		else
			return ele;
	}
	public String outField() {
		if(this.type==SearchType.NUMBER_RANGE||this.type==SearchType.HIDDEN_NUMBER_RANGE
				||this.type==SearchType.DATE_RANGE||this.type==SearchType.HIDDEN_DATE_RANGE
				||this.type==SearchType.DATETIME_RANGE||this.type==SearchType.HIDDEN_DATETIME_RANGE
				||this.type==SearchType.YEAR_RANGE||this.type==SearchType.HIDDEN_YEAR_RANGE) {
			return "'"+this.field+"_down':'','"+this.field+"_up':'',";
		}
		return "'"+this.field+"':'',";
	}
	@Override
	public String toString() {
		Element ele=toElement();
		return null==ele?"":ele.toString();
	}
}
